package org.example.registry;

import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.ChannelInboundHandlerAdapter;
import org.example.protocol.InvokerProtocol;

import java.io.File;
import java.lang.reflect.Method;
import java.net.URL;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;

public class RpcRegistryHandler extends ChannelInboundHandlerAdapter {
    private List<String> classNames = new ArrayList<>();
    private Map<String, Object> registryMap = new ConcurrentHashMap<>();


    public RpcRegistryHandler() {
        // 1、根据一个包名，将所有符合条件的class去拿不扫描出来，放到一个容器中（简化版本）
        // 如果是分布式，就读配置文件，dubbo 里面就有读取配置文件
        scannerClass("org.example.provider");

        // 2、给每一个对应的Class起一个唯一名字，作为服务名称，保存到一个容器中
        doRegistry();
    }

    private void doRegistry() {
        if (classNames == null) return;
        for (String className : classNames) {
            try {
                Class<?> clazz = Class.forName(className);
                Class<?> interfaceCls = clazz.getInterfaces()[0];
                String serviceName = interfaceCls.getName();
                // 本来这里存的应该是一个网络路径，从配置文件读取 ， 在调用的时候再去解析，
                // 这里用反射调用
                registryMap.put(serviceName, clazz.newInstance());
            } catch (ClassNotFoundException | IllegalAccessException | InstantiationException e) {
                e.printStackTrace();
            }
        }
    }

    // 正常来说，应该读取配置文件
    private void scannerClass(String packageName) {
        URL url = this.getClass().getClassLoader().getResource(packageName.replaceAll("\\.","/"));
        File classPath = new File(url.getFile());
        for (File file : classPath.listFiles()) {
            if (file.isDirectory()) {
                scannerClass(packageName + "." + file.getName());
            } else {
                classNames.add(packageName + "." + file.getName().replace(".class", ""));
            }
        }
    }


    // 有客户端连接上的时候就会回调
    // 3、当有客户端连接过来之后，就会获取协议内容 InvokerProtocol 的对象
    @Override
    public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
        Object result = new Object();
        if (msg instanceof InvokerProtocol) {
            InvokerProtocol request = (InvokerProtocol) msg;
            // 4、要去注册好的容器中找到符合条件的服务
            if (registryMap.containsKey(request.getClassName())) {
                Object service = registryMap.get(request.getClassName());
                Method method = service.getClass().getMethod(request.getMethodName(), request.getParames());
                result = method.invoke(service, request.getValues());
            }
            // 5、通过远程调用Provider得到返回结果，并回复给客户端
            ctx.write(result);
            ctx.flush();
            ctx.close();
        }
    }

    // 连接发生异常的时候就会回调
    @Override
    public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {
        System.out.println("Rpc Registry exception : " + cause.getMessage());
        super.exceptionCaught(ctx, cause);
    }

}
